﻿using NPOI.POIFS.Crypt.Dsig;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Core.Utility.Security
{
    /// <summary>
    /// Api签名安全数据
    /// </summary>
    public class ApiSignatureSecurityData : ApiSecurityData
    {
        /// <summary>
        /// 超时时间，单位:秒
        /// </summary>
        public long Timeout { get; private set; }

        /// <summary>
        /// Api签名安全数据
        /// </summary>
        /// <param name="token"></param>
        /// <param name="appId"></param>
        /// <param name="encodingAESKey"></param>
        /// <param name="timeout">超时时间，单位：秒</param>
        public ApiSignatureSecurityData(
            string? token, 
            string? appId, 
            string? encodingAESKey,
            long timeout = 30) : 
            base(token, appId, encodingAESKey)
        {
            Timeout = timeout;
        }

        /// <summary>
        /// 加密并签名
        /// </summary>
        /// <param name="input">明文</param>
        /// <returns>每次加密、签名产生的 timeStamp、nonce 不一样，注意存储及传参</returns>
        public ReturnResultInfo<ApiSignatureSecurityDataInfo> EncryptSignature(string? input)
        {
            ReturnResultInfo<ApiSignatureSecurityDataInfo> res = new ReturnResultInfo<ApiSignatureSecurityDataInfo>(message:"加密并签名失败");
            ApiSignatureSecurityDataInfo signatureSecurityDataInfo = new ApiSignatureSecurityDataInfo();
            var encryptRes = this.Encrypt(input);
            if(encryptRes.Successed)
            {
                signatureSecurityDataInfo.SetData(encryptRes.Data);
                var dataSignatureRes = ApiSecurity.Signature(Token, signatureSecurityDataInfo.TimeStamp, signatureSecurityDataInfo.Nonce, encryptRes.Data);
                if(dataSignatureRes.Successed)
                {
                    signatureSecurityDataInfo.SetDataSignature(dataSignatureRes.Data);
                    var signatureRes = ApiSecurity.Signature(Token, signatureSecurityDataInfo.TimeStamp, signatureSecurityDataInfo.Nonce, dataSignatureRes.Data);
                    if(signatureRes.Successed)
                    {
                        signatureSecurityDataInfo.SetSignature(signatureRes.Data);
                        res.SetSuccess(signatureSecurityDataInfo);
                    }
                    else
                    {
                        res = ReturnResultInfo.Convert<ApiSignatureSecurityDataInfo, string>(signatureRes);
                    }
                }
                else
                {
                    res = ReturnResultInfo.Convert<ApiSignatureSecurityDataInfo, string>(dataSignatureRes);
                }
            }
            else
            {
                res = ReturnResultInfo.Convert<ApiSignatureSecurityDataInfo, string>(encryptRes);
            }
            return res;
        }
        /// <summary>
        /// 验签并解密
        /// </summary>
        /// <param name="input">密文</param>
        /// <param name="hashSignature">验签用到的签名</param>
        /// <param name="hashDataSignature">验签用到的数据签名</param>
        /// <param name="timeStamp">时间戳</param>
        /// <param name="nonce">随机字符串</param>
        /// <param name="isVerifyAppId">是否验证AppId</param>
        /// <returns></returns>
        public ReturnResultInfo<string> DecryptVerifySignature(
            string? input,
            string? hashSignature,
            string? hashDataSignature,
            long? timeStamp,
            string? nonce,
            bool isVerifyAppId = true)
        {
            return DecryptVerifySignature(input, hashSignature, hashDataSignature, timeStamp, nonce, Timeout, isVerifyAppId);
        }


        /// <summary>
        /// 验签并解密
        /// </summary>
        /// <param name="input">密文</param>
        /// <param name="hashSignature">验签用到的签名</param>
        /// <param name="hashDataSignature">验签用到的数据签名</param>
        /// <param name="timeStamp">时间戳</param>
        /// <param name="nonce">随机字符串</param>
        /// <param name="timeout">超时时间</param>
        /// <param name="isVerifyAppId">是否验证AppId</param>
        /// <returns></returns>
        public ReturnResultInfo<string> DecryptVerifySignature(
            string? input, 
            string? hashSignature, 
            string? hashDataSignature,
            long? timeStamp, 
            string? nonce,
            long? timeout,
            bool isVerifyAppId = true)
        {
            ReturnResultInfo<string> res = new ReturnResultInfo<string>(message:"验签并解密失败");
            //1.验证数据签名
            var verifyDataSignatureRes = ApiSecurity.VerifySignature(hashDataSignature, Token, timeStamp?.ToString(), nonce, input, timeout);
            if(!verifyDataSignatureRes.Successed)
            {
                res = ReturnResultInfo.Convert<string>(verifyDataSignatureRes);
            }
            else
            {
                var verifySignatureRes = ApiSecurity.VerifySignature(hashSignature, Token, timeStamp?.ToString(), nonce, input, timeout);
                if (!verifySignatureRes.Successed)
                {
                    res = ReturnResultInfo.Convert<string>(verifySignatureRes); ;
                }
                else
                {
                    //解密
                    string? appId = "";
                    var decryptRes = ApiSecurity.AESDecrypt(input, EncodingAESKey, ref appId);
                    if (decryptRes.Successed)
                    {
                        if (isVerifyAppId)
                        {
                            if (AppId != appId)
                            {
                                decryptRes.SetFailure("解密获取的AppId与当前使用的AppId不一致，请查证！");
                            }
                        }
                    }
                    res = decryptRes;
                }
            }
            return res;
        }


        /// <summary>
        /// 设置超时时间
        /// </summary>
        /// <param name="value"></param>
        public void SetTimeOut(long value)
        {
            Timeout = value;
        }
    }
}
